package com.gxy.learn.backup.shiroauth.shiro.config;

import com.gxy.learn.backup.shiroauth.entity.SysUser;
import com.gxy.learn.backup.shiroauth.service.SysMenuService;
import com.gxy.learn.backup.shiroauth.service.SysRoleService;
import com.gxy.learn.backup.shiroauth.service.SysUserService;
import com.gxy.learn.backup.shiroauth.shiro.utils.JwtUtil;
import com.gxy.learn.backup.shiroauth.vo.SysUserRoleVO;
import com.gxy.learn.backup.shiroauth.vo.UserMenuVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.List;
import java.util.stream.Collectors;


/**
 * Description(自定义的ShiroRealm，用于进行用户请求验证及授权)
 * author: Gao xueyong
 * Create at: 2022/2/12 下午11:51
 */
public class JwtShiroRealm extends AuthorizingRealm {
    protected JwtVerifyTokenAuxiliary jwtVerifyTokenAuxiliary;
    protected SysUserService sysUserService;
    protected SysRoleService sysRoleService;
    protected SysMenuService sysMenuService;

    public JwtShiroRealm(SysUserService sysUserService, SysRoleService sysRoleService, SysMenuService sysMenuService, JwtVerifyTokenAuxiliary jwtVerifyTokenAuxiliary) {
        this.jwtVerifyTokenAuxiliary = jwtVerifyTokenAuxiliary;
        this.sysUserService = sysUserService;
        this.sysRoleService = sysRoleService;
        this.sysMenuService = sysMenuService;
//        this.setCredentialsMatcher(new JwtCredentialsMatcher());
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权，即向通过认证的用户赋予指定的权限
     * 每次通过Shiro获取权限/判断权限时均会触发此方法
     * 当配置缓存后，此方法在当次登录后，仅对当前用户仅调用一次(缓存未过期的情况下)
     * 当退出系统时，会自动清除缓存
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String token = (String) principals.getPrimaryPrincipal();
        String loginName = JwtUtil.getUsername(token);
        SysUser sysUser = sysUserService.getByLoginName(loginName);
        if (null == sysUser) {
            return new SimpleAuthorizationInfo();
        }
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        List<SysUserRoleVO> sysRoles = sysRoleService.getSysRolesByAccount(loginName);
        List<UserMenuVO> sysMenus = sysMenuService.getSysMenuByAccount(loginName);
        authorizationInfo.addRoles(sysRoles.stream().map(SysUserRoleVO::getRoleCode).collect(Collectors.toSet()));
        authorizationInfo.addStringPermissions(sysMenus.stream().map(UserMenuVO::getPermission).collect(Collectors.toSet()));
        return authorizationInfo;
    }


    /**
     * 认证信息.(身份验证) : Authentication 是用来验证用户身份
     * 默认使用此方法进行用户名正确与否验证，错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        JwtToken jwtToken = (JwtToken) authenticationToken;
        String token = jwtToken.getToken();
        /**
         * 校验token
         */
        if (StringUtils.isBlank(token) || !JwtUtil.verify(token)) {
            throw new IncorrectCredentialsException("token错误");
        }

        Integer loginVersion = JwtUtil.getLoginVersion(token);
        /**
         * 判断登录版本号
         */
        Integer currentVersion = jwtVerifyTokenAuxiliary.loginVersion(JwtUtil.getUsername(token));
        if (null == loginVersion || !loginVersion.equals(currentVersion)) {
            throw new AuthenticationException("token过期，请重新登录");
        }

        String username = JwtUtil.getUsername(token);
        SysUser customerUser = sysUserService.getByLoginName(username);
        if (null == customerUser) {
            throw new UnknownAccountException("token错误，请重新登录");
        }

        //此处无需对比,对比的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(token, token, getName());
        return authenticationInfo;
    }
}
